package org.sgmiller.quickstem.collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.sgmiller.quickstem.Stem;
import org.sgmiller.quickstem.Transaction;

/**
 * Implements a Java map collection, but whose contents are transactional. There
 * are no performance guarantees with this class.  Also, this map will LEAK KEYS
 * 
 * @author scott
 * 
 * @param <K>
 * @param <V>
 */
public class TransactionalMap<K, V> implements Map<K, V> {

	class Entry implements Map.Entry<K, V> {

		K key;
		V value;

		public Entry(K key, V value) {
			super();
			this.key = key;
			this.value = value;
		}
		
		@Override
		public K getKey() {
			return key;
		}

		@Override
		public V getValue() {
			return value;
		}

		@Override
		public V setValue(V value) {
			V old=this.value;
			this.value=value;
			return old;
		}
		
	}
	
	class EntrySet extends HashSet<Map.Entry<K, V>> {

		private static final long serialVersionUID = -5209309221576842765L;
		
	}
	
	HashMap<K, Stem<V>> values = new HashMap<K, Stem<V>>();
	Stem<Integer> size = new Stem<Integer>(0);

	@Override
	public void clear() {
		for (K key: values.keySet()) {
			remove(key);
		}
		size.set(0);
	}

	@Override
	public boolean containsKey(Object key) {
		return get(key) != null;
	}

	@Override
	public boolean containsValue(Object value) {
		for (V v: values()) {
			if (v.equals(value)) {
				return true;
			}
		}
		return false;
	}
	
	@Override
	public Set<Map.Entry<K, V>> entrySet() {
		Transaction current=Transaction.current();
		Set<Map.Entry<K, V>> returnSet=new EntrySet();
		for (Map.Entry<K,Stem<V>> e: values.entrySet()) {
			returnSet.add(new Entry(e.getKey(), current.get(e.getValue())));
		}
		return returnSet;
	}

	@Override
	public V get(Object key) {
		return values.get(key).get();
	}

	@Override
	public boolean isEmpty() {
		return size()>0;
	}

	@Override
	public Set<K> keySet() {
		Set<K> rv=new HashSet<K>();
		for (K key: values.keySet()) {
			if (containsKey(key)) {
				rv.add(key);
			}
		}
		return rv; 
	}

	@Override
	public V put(K key, V value) {
		Transaction current=Transaction.current();

		V old=null;
		Stem<V> v=values.get(key);
		if (v!=null) {
			old=current.get(v);
			if (old==null) size.set(current, current.get(size)+1);
			v.set(current, value);
		} else {
			values.put(key, new Stem<V>(value));
			size.set(current, current.get(size)+1);			
		}
		return old;
	}

	@Override
	public void putAll(Map<? extends K, ? extends V> m) {
		for (Map.Entry<? extends K,? extends V> e: m.entrySet()) {
			put(e.getKey(), e.getValue());
		}
	}

	@Override
	public V remove(Object key) {
		Transaction current=Transaction.current();

		V old=null;
		Stem<V> v=values.get(key);
		if (v!=null) {
			old=current.get(v);
			if (old!=null) size.set(current, current.get(size)-1);
			v.set(current, null);
		} 
		return old;
	}

	@Override
	public int size() {
		return size.get();
	}

	@Override
	public Collection<V> values() {
		Transaction current=Transaction.current();

		List<V> rv=new ArrayList<V>();
		for (Stem<V> v: values.values()) {
			if (v.get()!=null)
				rv.add(current.get(v));
		}
		return rv;
	}
	

	public static void main(String[] args) throws Exception {
		Map<String, Integer> m=new TransactionalMap<String, Integer>();
		long start=System.currentTimeMillis();
		for (int i=0; i<100000; i++) {
			for (int j=0; j<100; j++) {
				m.put("key"+j, i);
			}
		}
		System.out.println(m.get("key59"));
		System.out.println(m.size());
		System.out.println((System.currentTimeMillis()-start));		
	}
}
